// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2016-2019, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * memctrl config driver - Config of LomboTech Memctrl Controller
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>

#include <mach/debug.h>
#include <mach/common.h>
#include "memctrl.h"
#include "memctrl_conf.h"
#include "memctrl_dev.h"


/* redefine mode name, for PTR_XXX */
#undef MOD_NAME
#define MOD_NAME	"MEMCTRl_CONF"

static struct device *g_memctrl_dev;

static ssize_t memctrl_size_unit_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int i;
	ssize_t offset;
	int count;
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct data_unit *data_units =
				mctrl->data_unit_array->data_units;

	count = snprintf(buf, PAGE_SIZE, "memctrl current size unit:%s\n",
				data_units[mctrl->size_unit].name);
	offset = count;

	count = snprintf(buf + offset, PAGE_SIZE - offset,
					"you can also choose:\n");
	offset += count;
	for (i = 0; i < mctrl->data_unit_array->array_size; ++i) {
		if (i == mctrl->size_unit)
			continue;
		count = snprintf(buf + offset, PAGE_SIZE - offset,
					"unit=%s\n", data_units[i].name);
		offset += count;
	}

	count = snprintf(buf + offset, PAGE_SIZE - offset,
					"useage: echo 'unit' > memctrl_size_unit\n");
	offset += count;

	return offset;
}

static ssize_t memctrl_size_unit_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const char *unit;
	int i;
	const struct data_unit *data_units =
				mctrl->data_unit_array->data_units;

	for (i = 0; i < mctrl->data_unit_array->array_size; i++) {
		unit = data_units[i].name;
		if (!strncmp(unit, buf, count - 1))
			break;
	}

	if (i >= mctrl->data_unit_array->array_size)
		return -EINVAL;
	mctrl->size_unit = i;

	return count;
}

static DEVICE_ATTR_RW(memctrl_size_unit);

static ssize_t memctrl_flaw_unit_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int i;
	ssize_t offset;
	int count;
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct cfg_unit *flaw_units = mctrl->flaw_unit_array->flaw_units;

	count = snprintf(buf, PAGE_SIZE, "memctrl current flaw unit:%sB\n",
				flaw_units[mctrl->flaw_unit].name);
	offset = count;

	count = snprintf(buf + offset, PAGE_SIZE - offset,
						"you can also choose:\n");
	offset += count;

	for (i = 0; i < mctrl->flaw_unit_array->array_size; ++i) {
		if (i == mctrl->flaw_unit)
			continue;
		count = snprintf(buf + offset, PAGE_SIZE - offset,
					"unit=%s\n", flaw_units[i].name);
		offset += count;
	}

	count = snprintf(buf + offset, PAGE_SIZE - offset,
					"useage: echo 'unit' > memctrl_flaw_unit\n");
	offset += count;

	return offset;
}

static ssize_t memctrl_flaw_unit_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct cfg_unit *flaw_units = mctrl->flaw_unit_array->flaw_units;
	const char *unit;
	int i;

	for (i = 0; i < mctrl->flaw_unit_array->array_size; i++) {
		unit = flaw_units[i].name;
		if (!strncmp(unit, buf, count - 1))
			break;
	}

	if (i >= mctrl->flaw_unit_array->array_size)
		return -EINVAL;
	mctrl->flaw_unit = i;

	return count;
}

static DEVICE_ATTR_RW(memctrl_flaw_unit);

static ssize_t memctrl_sample_unit_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int i;
	ssize_t offset;
	char sample_unit[64];
	const char *sample_format = " [name:%s] : [value:%d]";
	int count;
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct cfg_unit *sample_units =
				mctrl->sample_unit_array->sample_units;

	snprintf(sample_unit, sizeof(sample_unit), sample_format,
			sample_units[mctrl->sample_unit].name,
			sample_units[mctrl->sample_unit].size
			/ sample_units[mctrl->sample_unit].div);

	count = snprintf(buf, PAGE_SIZE, "memctrl current sample unit:%s\n",
				sample_unit);
	offset = count;

	count = snprintf(buf + offset, PAGE_SIZE - offset,
						"you can also choose:\n");
	offset += count;

	for (i = 0; i < mctrl->sample_unit_array->array_size; ++i) {
		if (i == mctrl->sample_unit)
			continue;

		snprintf(sample_unit, sizeof(sample_unit), sample_format,
				sample_units[i].name,
				sample_units[i].size
				/ sample_units[i].div);

		count = snprintf(buf + offset, PAGE_SIZE - offset,
					"%s\n", sample_unit);
		offset += count;
	}

	count = snprintf(buf + offset, PAGE_SIZE - offset,
				"useage:echo 'name' > memctrl_sample_unit\n");
	offset += count;

	return offset;
}

static ssize_t memctrl_sample_unit_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const char *name;
	int i;

	for (i = 0; i < mctrl->sample_unit_array->array_size; i++) {
		name = mctrl->sample_unit_array->sample_units[i].name;
		if (!strncmp(buf, name, count - 1))
			break;
	}

	if (i >= mctrl->sample_unit_array->array_size)
		return -EINVAL;
	mctrl->sample_unit = i;

	return count;
}

static DEVICE_ATTR_RW(memctrl_sample_unit);

static ssize_t memctrl_time_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	char time_unit[10];
	const char *time_format = "%d%s";
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct cfg_unit *sample_units =
				mctrl->sample_unit_array->sample_units;

	snprintf(time_unit, sizeof(time_unit), time_format,
			sample_units[mctrl->sample_unit].size * mctrl->time
			/ sample_units[mctrl->sample_unit].div,
			sample_units[mctrl->sample_unit].unit);

	return snprintf(buf, PAGE_SIZE, "memctrl current time:%s\n",
								time_unit);
}

static ssize_t memctrl_time_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	unsigned long val;

	if (kstrtoul(buf, 0, &val))
		return -EINVAL;
	mctrl->time = val;

	return count;
}

static DEVICE_ATTR_RW(memctrl_time);

static ssize_t memctrl_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	size_t size;

	pm_runtime_get_sync(g_memctrl_dev);
	size = __memctrl_show(dev, attr, buf);
	pm_runtime_put_sync(g_memctrl_dev);

	return size;
}

static ssize_t memctrl_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	return count;
}

static DEVICE_ATTR_RW(memctrl);

static int master_2_channel(const char *master, struct master_pos *pos)
{
	int i, j;
	const struct master_info *memtrl_master_prio
			= mctrl_dev_get_memtrl_master_prio()->master_prio;
	u32 memtrl_master_prio_szie =
			mctrl_dev_get_memtrl_master_prio()->array_size;

	if (master == NULL)
		return -1;

	for (i = 0; i < memtrl_master_prio_szie; i++) {
		for (j = 0; j < memtrl_master_prio[i].master_num; ++j) {
			if (!strcmp(master, memtrl_master_prio[i].name[j])) {
				pos->level0 = i;
				pos->level1 = j;
				return 0;
			}
		}
	}

	return -1;
}

#ifdef CONFIG_ARCH_LOMBO_N7V7
static u32 memctrl_get_sarb_qos_sr(struct lombo_memctrl *mctrl,
					struct master_pos *pos, bool write)
{
	const struct master_info *memtrl_master_prio =
					mctrl->master_prio_array->master_prio;
	u32 regval, ret, w_offset, r_offset;
	int bit_offset, bit_mask;
	void *regbase;

	regbase = mctrl->sub_arbiter_regbase +
		memtrl_master_prio[pos->level0].qos_sr_root.reg_offset;
	bit_offset =
		memtrl_master_prio[pos->level0].qos_sr_root.bit_offset;

	bit_mask =
		memtrl_master_prio[pos->level0].qos_sr_root.bit_mask;

	regval = readl(regbase);

	if (write) {
		w_offset =
		memtrl_master_prio[pos->level0].qos_sr_sub[pos->level1].bit_offset + 16;

		ret = regval & (1 << w_offset);
	} else {
		r_offset =
		memtrl_master_prio[pos->level0].qos_sr_sub[pos->level1].bit_offset;

		ret = regval & (1 << r_offset);
	}

	return (ret > 0) ? 1 : 0;
}

static void memctrl_set_sarb_qos_sr(struct lombo_memctrl *mctrl,
					struct master_pos *pos, u32 sarb_sr, bool write)
{
	const struct master_info *memtrl_master_prio =
				mctrl->master_prio_array->master_prio;
	u32 val, offset;
	void *regbase;

	regbase = mctrl->sub_arbiter_regbase +
			memtrl_master_prio[pos->level0].qos_sr_root.reg_offset;

	if (write)
		offset =
		memtrl_master_prio[pos->level0].qos_sr_sub[pos->level1].bit_offset + 16;
	else
		offset =
		memtrl_master_prio[pos->level0].qos_sr_sub[pos->level1].bit_offset;

	val = readl(regbase) & (~(1 << offset));
	val = val | (sarb_sr << offset);

	writel(val, regbase);
}

/* get memctel source val of p_id */
static u32 memctrl_get_mctrl_qos_sr(struct lombo_memctrl *mctrl,
						int ch, u32 p_id, bool write)
{
	u32 ret_val;

	ret_val = csp_memctrl_get_mctrl_qos_sr(mctrl->reg[ch], write);

	ret_val = write ? (ret_val & (1 << (p_id + 16))) :
			(ret_val & (1 << p_id));

	return (ret_val > 0) ? 1 : 0;
}

static void memctrl_set_mctrl_qos_sr(struct lombo_memctrl *mctrl, int ch,
					struct master_pos *pos,
					u32 mctrl_sr, bool write)
{
	csp_memctrl_set_mctrl_qos_sr(mctrl->reg[ch], pos->level0, mctrl_sr, write);
}

static u32 memctrl_get_sarb_qos_val(struct lombo_memctrl *mctrl,
				struct master_pos *pos, const struct master_info *reg,
				bool write)
{
	int bit_offset;
	void *regbase;
	u32 ret_val;

	regbase = mctrl->sub_arbiter_regbase + (write ?
		(reg[pos->level0].qos_sr_root.rd_reg_offset + 0x4) :
			reg[pos->level0].qos_sr_root.rd_reg_offset);

	bit_offset = reg[pos->level0].qos_sr_sub[pos->level1].rd_bit_offset;

	ret_val = (readl(regbase) & (0xf << bit_offset)) >> bit_offset;

	return ret_val;
}

static void memctrl_set_sarb_qos_val(struct lombo_memctrl *mctrl,
					struct master_pos *pos, u32 sarb_val,
					const struct master_info *reg, bool write)
{
	int bit_offset;
	void *regbase;
	u32 val;

	regbase = mctrl->sub_arbiter_regbase + (write ?
		(reg[pos->level0].qos_sr_root.rd_reg_offset + 0x4) :
			reg[pos->level0].qos_sr_root.rd_reg_offset);

	bit_offset = reg[pos->level0].qos_sr_sub[pos->level1].rd_bit_offset;

	val = readl(regbase) & (~(0xf << bit_offset));
	val = val | (sarb_val << bit_offset);

	writel(val, regbase);
}

static u32 memctrl_get_mctrl_qos_val(struct lombo_memctrl *mctrl, int ch,
				struct master_pos *pos, bool write)
{
	u32 ret_val, bit_offset;
	void *regbase;

	if (pos->level0 < 8) {
		regbase = mctrl->reg[ch] + (write ?
			MEMCTRL_MCTRL_WQOS_CFG_OFFSET :
			MEMCTRL_MCTRL_RQOS_CFG_OFFSET);

		bit_offset = 0xf << ((pos->level0) * 4);

		ret_val = (readl(regbase) & bit_offset)
					>> ((pos->level0) * 4);
	} else {
		regbase = mctrl->reg[ch] + (write ?
			(MEMCTRL_MCTRL_WQOS_CFG_OFFSET + 0x4) :
			(MEMCTRL_MCTRL_RQOS_CFG_OFFSET + 0x4));

		if (pos->level0 == 8)
			ret_val = readl(regbase) & 0xf;

		if (pos->level0 == 9)
			ret_val = (readl(regbase) & (0xf << 4)) >> 4;
	}

	return ret_val;
}

static u32 memctrl_set_mctrl_qos_val(struct lombo_memctrl *mctrl, int ch,
			struct master_pos *pos, u32 qos_val, bool write)
{
	void *regbase;
	u32 val;

	if (pos->level0 < 8) {
		regbase = mctrl->reg[ch] + (write ?
			MEMCTRL_MCTRL_WQOS_CFG_OFFSET :
			MEMCTRL_MCTRL_RQOS_CFG_OFFSET);

		val = readl(regbase) & (~(0xf << (pos->level0 * 4)));
		val |= (qos_val << (pos->level0 * 4));

		writel(val, regbase);
	} else {
		regbase = mctrl->reg[ch] + (write ?
			(MEMCTRL_MCTRL_WQOS_CFG_OFFSET + 0x4) :
			(MEMCTRL_MCTRL_RQOS_CFG_OFFSET + 0x4));

		if (pos->level0 == 8) {
			val = readl(regbase) & (~0xf);
			val |= qos_val;
		}

		if (pos->level0 == 9) {
			val = readl(regbase) & (~0xf0);
			val |= (qos_val << 4);
		}

		writel(val, regbase);
	}
	return 0;
}
#endif /* CONFIG_ARCH_LOMBO_N7V7 */

static void memctrl_get_priority(struct lombo_memctrl *mctrl,
				struct master_pos *pos,
				struct master_priority *prio, bool write)
{
	const struct master_info *memtrl_master_prio =
				mctrl->master_prio_array->master_prio;
	int reg_offset, bit_mask, bit_offset, i;
#ifdef CONFIG_ARCH_LOMBO_N7V7
	u32 ret_val = -1, ret = -1, ret_sarb;
#endif
	void *regbase;

	for (i = 0; i < mctrl->ch_number; ++i) {
		/* read priority level one fistly */
		/* 0x4000 is offset of priority ch_1 addr */
		regbase = mctrl->ddr_bitbase[i] + (i * 0x4000) +
		(write ?
		MEMCTRL_AXI_WQOS0_OFFSET : MEMCTRL_AXI_RQOS0_OFFSET) +
		memtrl_master_prio[pos->level0].priority_root.reg_offset;

		bit_offset =
		memtrl_master_prio[pos->level0].priority_root.bit_offset;

		bit_mask =
		memtrl_master_prio[pos->level0].priority_root.bit_mask;

		prio->level0 = readl(regbase);

		/* timeout is not open, prio is lowest */
		if ((prio->level0 & 0x1000) != 0x1000)
			prio->level0 = -1;
		else
			prio->level0 = (prio->level0 >> bit_offset) & bit_mask;

		/* then read the level1, no sarb*/
		if (memtrl_master_prio[pos->level0].master_num == 1) {
			prio->level1 = -1;
#ifdef CONFIG_ARCH_LOMBO_N7V7
			/* qos way to get priority*/
			ret_val = memctrl_get_mctrl_qos_sr(mctrl, i, pos->level0, write);

			if (ret_val == 0) { /* form memctrl */
				ret = memctrl_get_mctrl_qos_val(mctrl, i, pos, write);
				prio->qos_val = ret;
			} else if (ret_val == 1) { /* from ip */
				prio->qos_val = -1;
			}
#endif
		} else if (memtrl_master_prio[pos->level0].master_num > 1) {
			reg_offset =
	memtrl_master_prio[pos->level0].priority_sub[pos->level1].reg_offset;

			bit_offset =
	memtrl_master_prio[pos->level0].priority_sub[pos->level1].bit_offset;

			bit_mask =
	memtrl_master_prio[pos->level0].priority_sub[pos->level1].bit_mask;

			prio->level1 =
				readl(mctrl->sub_arbiter_regbase + reg_offset);
			prio->level1 =
				(prio->level1 >> bit_offset) & bit_mask;
#ifdef CONFIG_ARCH_LOMBO_N7V7
			/* qos way to get priority*/
			ret_sarb = memctrl_get_sarb_qos_sr(mctrl, pos, write);

			if (ret_sarb == 0) { /* form sarb */
				ret = memctrl_get_sarb_qos_val(mctrl, pos,
							memtrl_master_prio, write);
				prio->qos_val = ret;
			} else if (ret_sarb == 1) { /* from ip */
				prio->qos_val = -1;
			}
#endif
		}
		/* no need to read other channels */
		return;
	}
}

static void memctrl_set_priority(struct lombo_memctrl *mctrl,
	struct master_pos *pos, struct master_priority *prio, bool write)
{

	const struct master_info *memtrl_master_prio =
				mctrl->master_prio_array->master_prio;
	int i, reg_offset, bit_offset, bit_mask;
	void *regbase;
	u32 value;

	for (i = 0; i < mctrl->ch_number; ++i) {
		/* write priority level0 fistly */
		/* 0xa000 is offset of memctrl addr and priority addr */
		regbase = mctrl->ddr_bitbase[i] + (i * 0x4000) +
		(write ?
		MEMCTRL_AXI_WQOS0_OFFSET : MEMCTRL_AXI_RQOS0_OFFSET) +
		memtrl_master_prio[pos->level0].priority_root.reg_offset;

		bit_offset =
		memtrl_master_prio[pos->level0].priority_root.bit_offset;

		bit_mask =
		memtrl_master_prio[pos->level0].priority_root.bit_mask;
		if (prio->level0 != -1) {
			value = readl(regbase);
			value &= ~(bit_mask << bit_offset);
			value |= prio->level0 << bit_offset;
			value |= (1 << 12);
			writel(value, regbase);
		}

		/* then write the level1, no sarb */
		if (memtrl_master_prio[pos->level0].master_num == 1) {
#ifdef CONFIG_ARCH_LOMBO_N7V7
			if (prio->qos_val != -1)
				memctrl_set_mctrl_qos_val(mctrl, i,
							pos, prio->qos_val, write);
#endif
			continue;
		} else if (memtrl_master_prio[pos->level0].master_num > 1) {
			reg_offset =
	memtrl_master_prio[pos->level0].priority_sub[pos->level1].reg_offset;

			bit_offset =
	memtrl_master_prio[pos->level0].priority_sub[pos->level1].bit_offset;

			bit_mask =
	memtrl_master_prio[pos->level0].priority_sub[pos->level1].bit_mask;
			if (prio->level1 != -1) {
				value = readl(mctrl->sub_arbiter_regbase + reg_offset);
				value &= ~(bit_mask << bit_offset);
				value |= prio->level1 << bit_offset;
				writel(value, (mctrl->sub_arbiter_regbase + reg_offset));
			}
#ifdef CONFIG_ARCH_LOMBO_N7V7
			/* qos way to set priority*/
			if (prio->qos_val != -1) {
				memctrl_set_mctrl_qos_sr(mctrl, i, pos, 1, write);
				memctrl_set_sarb_qos_val(mctrl, pos, prio->qos_val,
								memtrl_master_prio, write);
			}
#endif
		}
	}
}

static ssize_t lombo_memctrl_priority_show(struct lombo_memctrl *mctrl,
						char *buf)
{
	int i, j, k = 6;
	char *level0, *level1, *qos;
	ssize_t count, offset = 0;
	struct master_pos pos;
	struct master_priority prio;
	const struct master_info *master_prio
			= mctrl_dev_get_memtrl_master_prio()->master_prio;
	u32 master_prio_szie =
			mctrl_dev_get_memtrl_master_prio()->array_size;

	level0 = kmalloc(sizeof(char) * 4, GFP_KERNEL);
	level1 = kmalloc(sizeof(char) * 4, GFP_KERNEL);
	qos = kmalloc(sizeof(char) * 4, GFP_KERNEL);

#ifdef CONFIG_ARCH_LOMBO_N7V7
	count = snprintf(buf, PAGE_SIZE,
			"%-10s %10s %10s %10s %10s\n",
			"MASTER", "MODE", "LEVEL0", "LEVEL1", "QOS");
	offset += count;
#else
	count = snprintf(buf, PAGE_SIZE,
			"%-10s %10s %10s %10s\n",
			"MASTER", "MODE", "LEVEL0", "LEVEL1");
	offset += count;
#endif
	/* Split line */
	while (k--) {
		count = snprintf(buf + offset, PAGE_SIZE - offset,
				"%s", "---------");
		offset += count;
	}
	count = snprintf(buf + offset, PAGE_SIZE - offset, "\n");
	offset += count;
	for (i = 0; i < master_prio_szie; i++) {
		for (j = 0; j < master_prio[i].master_num; ++j) {
			pos.level0 = i;
			pos.level1 = j;
			memctrl_get_priority(mctrl, &pos, &prio, false);
			snprintf(level0, 5, "%d", prio.level0);
			snprintf(level1, 4, "%d", prio.level1);
			snprintf(qos, 4, "%d", prio.qos_val);

#ifdef CONFIG_ARCH_LOMBO_N7V7
			count = snprintf(buf + offset, PAGE_SIZE - offset,
				"%-10s %10s %10s %10s %10s\n",
				master_prio[i].name[j], "read",
				(prio.level0 == -1) ? "lowest" : level0,
				(prio.level1 == -1) ? "N/A" : level1,
				(prio.qos_val == -1) ? "IP" : qos);
#else
			count = snprintf(buf + offset, PAGE_SIZE - offset,
				"%-10s %10s %10s %10s\n",
				master_prio[i].name[j], "read",
				(prio.level0 == -1) ? "lowest" : level0,
				(prio.level1 == -1) ? "N/A" : level1);
#endif
			offset += count;
			memctrl_get_priority(mctrl, &pos, &prio, true);
			snprintf(level0, 5, "%d", prio.level0);
			snprintf(level1, 4, "%d", prio.level1);
			snprintf(qos, 4, "%d", prio.qos_val);

#ifdef CONFIG_ARCH_LOMBO_N7V7
			count = snprintf(buf + offset, PAGE_SIZE - offset,
				"%-10s %10s %10s %10s %10s\n",
				"", "write",
				(prio.level0 == -1) ? "lowest" : level0,
				(prio.level1 == -1) ? "N/A" : level1,
				(prio.qos_val == -1) ? "IP" : qos);
#else
			count = snprintf(buf + offset, PAGE_SIZE - offset,
				"%-10s %10s %10s %10s\n",
				"", "write",
				(prio.level0 == -1) ? "lowest" : level0,
				(prio.level1 == -1) ? "N/A" : level1);
#endif
			offset += count;
		}
	}
	kfree(level0);
	kfree(level1);
	kfree(qos);
	return offset;
}

static void lombo_memctrl_priority_help(struct lombo_memctrl *mctrl)
{
#ifdef CONFIG_ARCH_LOMBO_N7V1
	pr_warn("help:\n %s %s %s %s %s %s %s",
	"read/write priority of masters, low level[0~1023] is higher priority\n",
	"echo <master> > get_read_prioriry\n",
	"echo <master>,L0=<level> > set_read_prioriry\n",
	"echo <master> > get_write_prioriry\n",
	"echo <master>=<level> > set_write_prioriry\n",
	"master:CPU, VC, AX, VISS0, VISS1, DPU, DMA, USB, SDC0_2, SDC1\n",
	"example: echo CPU,L0=1 > set_read_prioriry\n");
#else
	int i, j;
	const struct master_info *master_prio
			= mctrl_dev_get_memtrl_master_prio()->master_prio;
	u32 master_prio_array_size =
			mctrl_dev_get_memtrl_master_prio()->array_size;

	/* we need to print the priority level archtechture firstly */
	pr_warn("help:\n");
	pr_warn("--------------------\n");
	pr_warn("  %-8s %10s\n", "AXI_PORT", "MASTER");

	for (i = 0; i < master_prio_array_size; ++i)
		for (j = 0; j < master_prio[i].master_num; ++j)
			pr_warn("  %-8u %10s\n", i,
				master_prio[i].name[j]);
	pr_warn("--------------------\n\n");

	/* then print the usage */
	pr_warn(" %s %s %s %s %s",
		"read/write priority of masters, high level0[0~1023] level1[0-7],QOS[0-15] has higher priority\n",
		"echo <master> > get_read_prioriry\n",
		"echo <master>,L0=<level0>,L1=<level1>,QOS=<QOS> > set_read_prioriry\n",
		"echo <master> > get_write_prioriry\n",
		"echo <master>,L0=<level0>,L1=<level1>,QOS=<QOS> > set_write_prioriry\n");
	pr_warn(" masters showed above\n");
	pr_warn(" example:\n");
	pr_warn(" echo MJPEG,L0=12,L1=4,QOS=1 > set_read_prioriry\n");
	pr_warn(" means: set the root priority of MJPEG to 12, and the sub priority if to 4, and the qos priority to 1\n\n");
	pr_warn(" if the master has not the sub priority, you can echo\n");
	pr_warn(" the root priority directly like:\n");
	pr_warn("   1、echo CPU,L0=12,QOS=1 > set_read_prioriry\n");
	pr_warn("   2、echo CPU,QOS=1 > set_read_prioriry\n");
	pr_warn("   3、echo CPU,L0=12 > set_read_prioriry\n");
	pr_warn(" means: set the root priority of CPU to 12, and no sarb priority, and the qos priority to 1\n\n");
	pr_warn(" QOS source setting example:\n");
	pr_warn(" if master doesn't have sarb, we should call this node: set_mctrl_sr\n");
	pr_warn("   echo CPU R/W=1/0 > set_mctrl_sr; 1 --> from ip , 0 --> from mctrl qos val\n");
	pr_warn(" if master have sarb, we should call this node: set_sarb_sr");
	pr_warn("   echo MJPEG R/W=1/0 > set_sarb_sr; 1 --> from ip , 0 --> from sarb qos val\n");
#endif
}

static ssize_t set_read_priority_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	size_t size;
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);

	pm_runtime_get_sync(g_memctrl_dev);
	size = lombo_memctrl_priority_show(mctrl, buf);
	pm_runtime_put_sync(g_memctrl_dev);

	return size;
}

static ssize_t set_read_priority_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct master_info *memtrl_master_prio =
				mctrl->master_prio_array->master_prio;
	char *val, *ptr0, *ptr1, *ptr2, *temp;
	struct master_priority prio;
	struct master_pos pos;
	char *l0, *l1;
	int ret;
#ifdef CONFIG_ARCH_LOMBO_N7V7
	char *qos;
#endif

	pm_runtime_get_sync(g_memctrl_dev);

	prio.level0 = -1;
	prio.level1 = -1;
	prio.qos_val = -1;

	val = strchr(buf, ',');
	if (!val)
		goto exit;

	/* replace ',' width '\0' */
	*(val++) = '\0';

	ret = master_2_channel(buf, &pos);
	if (ret == -1)
		goto exit;

	ptr0 = strstr(val, "QOS");
	ptr1 = strstr(val, "L1");
	ptr2 = strstr(val, "L0");

	if (memtrl_master_prio[pos.level0].master_num == 1 && ptr1 != NULL) {
		pr_warn(" this master has no sarb, please enter like:\n");
		pr_warn(" CPU,L0=<0-1023>\nCPU,L0=<0-1023>,QOS=<0-15>\nCPU,QOS=<0-15>\n");
		return count;
	}

	if (ptr1 && !ptr2)
		goto exit;

#ifdef CONFIG_ARCH_LOMBO_N7V7
	if (ptr0) {
		qos = strchr(ptr0, '=');
		qos++;
		if (kstrtouint(qos, 0, &prio.qos_val))
			goto exit;
		if (prio.qos_val < 0 || prio.qos_val > 15)
			goto exit;
	}
#endif
	if (ptr1) {
		temp = strchr(ptr1, ',');
		if (temp)
			*temp = '\0';
		l1 = strchr(ptr1, '=');
		l1++;
		if (kstrtouint(l1, 0, &prio.level1))
			goto exit;

		if (prio.level1 < 0 || prio.level1 > 7)
			goto exit;
	}

	if (ptr2) {
		temp = strchr(ptr2, ',');
		if (temp)
			*temp = '\0';
		l0 = strchr(ptr2, '=');
		l0++;
		if (kstrtouint(l0, 0, &prio.level0))
			goto exit;

		if (prio.level0 < 0 || prio.level0 > 1023)
			goto exit;
	}

	memctrl_set_priority(mctrl, &pos, &prio, false);

	pm_runtime_put_sync(g_memctrl_dev);

	return count;
exit:
	lombo_memctrl_priority_help(mctrl);

	pm_runtime_put_sync(g_memctrl_dev);

	return count;
}
static DEVICE_ATTR_RW(set_read_priority);

static ssize_t set_write_priority_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	size_t size;
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);

	pm_runtime_get_sync(g_memctrl_dev);

	size = lombo_memctrl_priority_show(mctrl, buf);

	pm_runtime_put_sync(g_memctrl_dev);

	return size;
}

static ssize_t set_write_priority_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct master_info *memtrl_master_prio =
				mctrl->master_prio_array->master_prio;
	char *val, *ptr0, *ptr1, *ptr2, *temp;
	struct master_priority prio;
	struct master_pos pos;
	char *l0, *l1;
	int ret;
#ifdef CONFIG_ARCH_LOMBO_N7V7
	char *qos;
#endif

	pm_runtime_get_sync(g_memctrl_dev);

	prio.level0 = -1;
	prio.level1 = -1;
	prio.qos_val = -1;

	val = strchr(buf, ',');
	if (!val)
		goto exit;

	/* replace ',' width '\0' */
	*(val++) = '\0';

	ret = master_2_channel(buf, &pos);
	if (ret == -1)
		goto exit;

	ptr0 = strstr(val, "QOS");
	ptr1 = strstr(val, "L1");
	ptr2 = strstr(val, "L0");

	if (memtrl_master_prio[pos.level0].master_num == 1 && ptr1 != NULL) {
		pr_warn("this master has no sarb, please enter like:\n");
		pr_warn(" CPU,L0=<0-1023>,QOS=<0-15>\n CPU,QOS=<0-15>\n");
		return count;
	}

	if (ptr1 && !ptr2)
		goto exit;

#ifdef CONFIG_ARCH_LOMBO_N7V7
	if (ptr0) {
		qos = strchr(ptr0, '=');
		qos++;
		if (kstrtouint(qos, 0, &prio.qos_val))
			goto exit;
		if (prio.qos_val < 0 || prio.qos_val > 15)
			goto exit;
	}
#endif

	if (ptr1) {
		temp = strchr(ptr1, ',');
		if (temp)
			*temp = '\0';
		l1 = strchr(ptr1, '=');
		l1++;
		if (kstrtouint(l1, 0, &prio.level1))
			goto exit;

		if (prio.level1 < 0 || prio.level1 > 7)
			goto exit;
	}

	if (ptr2) {
		temp = strchr(ptr2, ',');
		if (temp)
			*temp = '\0';
		l0 = strchr(ptr2, '=');
		l0++;
		if (kstrtouint(l0, 0, &prio.level0))
			goto exit;

		if (prio.level0 < 0 || prio.level0 > 1023)
			goto exit;
	}

	memctrl_set_priority(mctrl, &pos, &prio, true);

	pm_runtime_put_sync(g_memctrl_dev);
	return count;
exit:
	lombo_memctrl_priority_help(mctrl);

	pm_runtime_put_sync(g_memctrl_dev);
	return count;
}

static DEVICE_ATTR_RW(set_write_priority);

#ifdef CONFIG_ARCH_LOMBO_N7V7
static ssize_t set_sarb_sr_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return 0;
}

static ssize_t set_sarb_sr_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct master_info *memtrl_master_prio =
				mctrl->master_prio_array->master_prio;
	struct master_pos pos;
	int ret, sr, len, i;
	char master[16], wr;

	pm_runtime_get_sync(g_memctrl_dev);

	len = strlen(buf);
	if (len > sizeof(master))
		goto exit;

	if (sscanf(buf, "%s %c=%d", master, &wr, &sr) != 3)
		goto exit;

	if (sr != 0 && sr != 1)
		goto exit;

	ret = master_2_channel(master, &pos);
	if (ret == -1)
		goto exit;


	if (memtrl_master_prio[pos.level0].master_num == 1) {
		pr_err("this master has no sarb, please use node: set_mctrl_sr!!\n");
		goto exit;
	}

	if (wr == 'R')
		memctrl_set_sarb_qos_sr(mctrl, &pos, sr, false);
	else if (wr == 'W')
		memctrl_set_sarb_qos_sr(mctrl, &pos, sr, true);
	else
		goto exit;

	for (i = 0; i < mctrl->ch_number; i++) {
		if (wr == 'R')
			memctrl_set_mctrl_qos_sr(mctrl, i, &pos, 1, false);
		else if (wr == 'W')
			memctrl_set_mctrl_qos_sr(mctrl, i, &pos, 1, true);
		else
			goto exit;
	}

	pm_runtime_put_sync(g_memctrl_dev);

	return count;
exit:
	lombo_memctrl_priority_help(mctrl);

	pm_runtime_put_sync(g_memctrl_dev);

	return count;
}
static DEVICE_ATTR_RW(set_sarb_sr);

static ssize_t set_mctrl_sr_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return 0;
}

static ssize_t set_mctrl_sr_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	const struct master_info *memtrl_master_prio =
			mctrl->master_prio_array->master_prio;
	struct master_pos pos;
	char master[10], wr;
	int i, ret, sr, len;

	len = strlen(buf);
	if (len > 10)
		goto exit;

	pm_runtime_get_sync(g_memctrl_dev);

	if (sscanf(buf, "%s %c=%d", master, &wr, &sr) != 3)
		goto exit;

	if (sr != 0 && sr != 1)
		goto exit;

	ret = master_2_channel(master, &pos);
	if (ret == -1)
		goto exit;

	if (memtrl_master_prio[pos.level0].master_num != 1) {
		pr_err("this master has sarb, please use node: set_sarb_sr!!\n");
		goto exit;
	}

	for (i = 0; i < mctrl->ch_number; i++) {
		if (wr == 'R')
			memctrl_set_mctrl_qos_sr(mctrl, i, &pos, sr, false);
		else if (wr == 'W')
			memctrl_set_mctrl_qos_sr(mctrl, i, &pos, sr, true);
		else
			goto exit;
	}

	pm_runtime_put_sync(g_memctrl_dev);

	return count;
exit:
	lombo_memctrl_priority_help(mctrl);

	pm_runtime_put_sync(g_memctrl_dev);

	return count;
}
static DEVICE_ATTR_RW(set_mctrl_sr);
#endif /* CONFIG_ARCH_LOMBO_N7V7 */

/**
 * lombo_memctrl_bw_limit - fix master band width
 *
 * @master: master name
 * @cycle: time window cycle
 * @band_width: data size in time window
 *
 * Returns value is 1 on success, a negative error code on failure.
 */
int lombo_memctrl_bw_ctrl_mode(const char *master, u32 ctrl_mode, int rw)
{
	int i;
	u32 arbiter, hwnum;
	void __iomem *sarb = lombo_get_sarb_addr();
	void __iomem *noc = lombo_get_noc_addr();
	const struct bwctrl_masters_array *masters =
			mctrl_dev_get_bwctrl_masters();

	if (ctrl_mode < 0 || ctrl_mode > 3)
		return -EINVAL;

	pm_runtime_get_sync(g_memctrl_dev);

	for (i = 0; i < masters->array_size; i++) {
		if (!strcmp(master, masters->bwctrl_masters[i].name)) {
			arbiter = masters->bwctrl_masters[i].arbiter;
			hwnum = masters->bwctrl_masters[i].hwnum;
			csp_memctrl_set_bw_ctrl_mode((arbiter ? sarb : noc),
				hwnum, ctrl_mode, rw, arbiter);
			pm_runtime_put_sync(g_memctrl_dev);
			return 0;
		}
	}
	pm_runtime_put_sync(g_memctrl_dev);
	PRT_ERR("master[%s] is not found\n", master);
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(lombo_memctrl_bw_ctrl_mode);

int lombo_memctrl_bw_randomode(const char *master, u32 randomode, int rw)
{
	int i;
	u32 arbiter, hwnum;
	void __iomem *sarb = lombo_get_sarb_addr();
	void __iomem *noc = lombo_get_noc_addr();
	const struct bwctrl_masters_array *masters =
			mctrl_dev_get_bwctrl_masters();

	if (randomode < 0 || randomode > 3)
		return -EINVAL;
	pm_runtime_get_sync(g_memctrl_dev);
	for (i = 0; i < masters->array_size; i++) {
		if (!strcmp(master, masters->bwctrl_masters[i].name)) {
			arbiter = masters->bwctrl_masters[i].arbiter;
			hwnum = masters->bwctrl_masters[i].hwnum;
			csp_memctrl_set_bw_randomode((arbiter ? sarb : noc),
				hwnum, randomode, rw, arbiter);
			pm_runtime_put_sync(g_memctrl_dev);
			return 0;
		}
	}
	pm_runtime_put_sync(g_memctrl_dev);
	PRT_ERR("master[%s] is not found\n", master);
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(lombo_memctrl_bw_randomode);

int lombo_memctrl_bw_seed(const char *master, u32 seed, int rw)
{
	int i;
	u32 arbiter, hwnum;
	void __iomem *sarb = lombo_get_sarb_addr();
	void __iomem *noc = lombo_get_noc_addr();
	const struct bwctrl_masters_array *masters =
			mctrl_dev_get_bwctrl_masters();

	if (seed < 0 || seed > 8388607)
		return -EINVAL;
	pm_runtime_get_sync(g_memctrl_dev);
	for (i = 0; i < masters->array_size; i++) {
		if (!strcmp(master, masters->bwctrl_masters[i].name)) {
			arbiter = masters->bwctrl_masters[i].arbiter;
			hwnum = masters->bwctrl_masters[i].hwnum;
			csp_memctrl_set_bw_seed((arbiter ? sarb : noc),
				hwnum, seed, rw, arbiter);
			pm_runtime_put_sync(g_memctrl_dev);
			return 0;
		}
	}
	pm_runtime_put_sync(g_memctrl_dev);
	PRT_ERR("master[%s] is not found\n", master);
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(lombo_memctrl_bw_seed);

int lombo_memctrl_bw_timewin(const char *master, u32 timewin, int rw)
{
	int i;
	u32 arbiter, hwnum;
	void __iomem *sarb = lombo_get_sarb_addr();
	void __iomem *noc = lombo_get_noc_addr();
	const struct bwctrl_masters_array *masters =
			mctrl_dev_get_bwctrl_masters();

	if (timewin <= 0) {
		PRT_ERR("master[%s] time window is too small\n", master);
		return -EINVAL;
	}
	pm_runtime_get_sync(g_memctrl_dev);
	for (i = 0; i < masters->array_size; i++) {
		if (!strcmp(master, masters->bwctrl_masters[i].name)) {
			arbiter = masters->bwctrl_masters[i].arbiter;
			hwnum = masters->bwctrl_masters[i].hwnum;
			csp_memctrl_set_bw_timewin((arbiter ? sarb : noc),
				hwnum, timewin, rw, arbiter);
			pm_runtime_put_sync(g_memctrl_dev);
			return 0;
		}
	}
	pm_runtime_put_sync(g_memctrl_dev);
	PRT_ERR("master[%s] is not found\n", master);
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(lombo_memctrl_bw_timewin);

int lombo_memctrl_bw_datasize(const char *master, u32 datasize, int rw)
{
	int i;
	u32 arbiter, hwnum;
	void __iomem *sarb = lombo_get_sarb_addr();
	void __iomem *noc = lombo_get_noc_addr();
	const struct bwctrl_masters_array *masters =
			mctrl_dev_get_bwctrl_masters();

	if (datasize <= 0) {
		PRT_ERR("master[%s] data size is too small\n", master);
		return -EINVAL;
	}
	pm_runtime_get_sync(g_memctrl_dev);
	for (i = 0; i < masters->array_size; i++) {
		if (!strcmp(master, masters->bwctrl_masters[i].name)) {
			arbiter = masters->bwctrl_masters[i].arbiter;
			hwnum = masters->bwctrl_masters[i].hwnum;
			csp_memctrl_set_bw_datasize((arbiter ? sarb : noc),
				hwnum, datasize, rw, arbiter);
			pm_runtime_put_sync(g_memctrl_dev);
			return 0;
		}
	}
	pm_runtime_put_sync(g_memctrl_dev);
	PRT_ERR("master[%s] is not found\n", master);
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(lombo_memctrl_bw_datasize);

int lombo_memctrl_get_bw_info(const char *buf, u32 *master_id, u32 *bits_id,
				long *val, u32 *arbiter)
{
	int i;
	char *master, *bits_name;
	const struct bwctrl_masters_array *masters =
					mctrl_dev_get_bwctrl_masters();
	const struct bw_bits_name_array *bwctrl_bits =
					mctrl_dev_get_bwctrl_bits();
	*master_id = -1;
	*arbiter = -1;
	*bits_id = -1;
	*val = -1;

	/* get master */
	for (i = 0; i < masters->array_size; i++) {
		master = strstr(buf, masters->bwctrl_masters[i].name);
		if (master) {
			master = (char *)masters->bwctrl_masters[i].name;
			*master_id = masters->bwctrl_masters[i].hwnum;
			*arbiter = masters->bwctrl_masters[i].arbiter;
			break;
		}
	}
	if (*master_id == -1 || *arbiter == -1)
		return 0;

	/* get bits_name */
	for (i = 0; i < bwctrl_bits->array_size; i++) {
		if (strstr(buf, bwctrl_bits->names[i].name) ||
				strstr(buf, bwctrl_bits->names[i].ref)) {
			bits_name = (char *)bwctrl_bits->names[i].name;
			*bits_id = i;
		}
	}
	if (*bits_id == -1)
		return 0;

	/* get val to be set */
	if (strstr(buf, "default") || strstr(buf, "def"))
		*val = bwctrl_bits->names[*bits_id].val;
	else {
		if (kstrtol((strrchr(buf, '-') + 1), 10, val) ||
					*val < 0)
			*val = -1;
	}
	if (*val == -1)
		return 0;

	return 1;
}

static void memctrl_bandwidth_limit_help(void)
{
	pr_err("%s\n%s%s%s\n%s\n",
		"usage: <master-bits_name-value> set band width limit",
		"bits_name:timewindow(tw),datasize(ds), randmode(rm),",
		"ctrlmode(cm),randomseed(rs).",
		"value set default(def) will reset bits",
		"e.g. echo AX0-tw-300 > memctrl_bandwidth_rd_limitor");
}

static ssize_t __memctrl_bandwidth_limit_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int i = 8;
	u32 arbiter, hwnum;
	ssize_t count, offset = 0;
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);
	void __iomem *noc = lombo_get_noc_addr();
	const struct bwctrl_masters_array *masters_array =
					mctrl_dev_get_bwctrl_masters();

	count = snprintf(buf, PAGE_SIZE,
			"%-10s %10s %10s %10s %10s %10s %10s\n",
			"MASTER", "MODE", "TW", "DS(Byte)", "RM", "CM", "RS");
	offset += count;
	/* Split line */
	while (i--) {
		count = snprintf(buf + offset, PAGE_SIZE - offset,
				"%s", "----------");
		offset += count;
	}
	count = snprintf(buf + offset, PAGE_SIZE - offset, "\n");
	offset += count;
	for (i = 0; i < masters_array->array_size; i++) {
		arbiter = masters_array->bwctrl_masters[i].arbiter;
		hwnum = masters_array->bwctrl_masters[i].hwnum;
		count = snprintf(buf + offset, PAGE_SIZE - offset,
			"%-10s %10s %10d %10d %10d %10d %10d\n",
			masters_array->bwctrl_masters[i].name, "read",
			csp_memctrl_get_bw_timewin(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, R, arbiter),
			csp_memctrl_get_bw_datasize(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, R, arbiter),
			csp_memctrl_get_bw_randomode(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, R, arbiter),
			csp_memctrl_get_bw_ctrl_mode(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, R, arbiter),
			csp_memctrl_get_bw_seed(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, R, arbiter));
		offset += count;
		count = snprintf(buf + offset, PAGE_SIZE - offset,
			"%-10s %10s %10d %10d %10d %10d %10d\n",
			" ", "write",
			csp_memctrl_get_bw_timewin(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, W, arbiter),
			csp_memctrl_get_bw_datasize(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, W, arbiter),
			csp_memctrl_get_bw_randomode(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, W, arbiter),
			csp_memctrl_get_bw_ctrl_mode(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, W, arbiter),
			csp_memctrl_get_bw_seed(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				hwnum, W, arbiter));
		offset += count;
	}

	return offset;
}

static int __memctrl_set_bandwidth(struct device *dev, const char *buf,
					enum memctrl_rw rw)
{
	u32 master_id, bits_id, arbiter;
	long val;
	void __iomem *noc = lombo_get_noc_addr();
	struct lombo_memctrl *mctrl = dev_get_drvdata(dev);

	if (!lombo_memctrl_get_bw_info(buf, &master_id, &bits_id,
				&val, &arbiter))
		return -EINVAL;

	switch (bits_id) {
	case BW_TIMEWINDOW:
		csp_memctrl_set_bw_timewin(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				master_id, val, rw, arbiter);
		break;
	case BW_DATASIZE:
		csp_memctrl_set_bw_datasize(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				master_id, val, rw, arbiter);
		break;
	case BW_RANDOMMODE:
		if (val < 0 || val > 3)
			return -EINVAL;
		csp_memctrl_set_bw_randomode(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				master_id, val, rw, arbiter);
		break;
	case BW_CTRLMODE:
		if (val < 0 || val > 3)
			return -EINVAL;
		csp_memctrl_set_bw_ctrl_mode(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				master_id, val, rw, arbiter);
		break;
	case BW_RANDOMSEED:
		if (val <= 0 || val > 8388607) /* 23 bit max number */
			return -EINVAL;
		csp_memctrl_set_bw_seed(
				((arbiter) ? mctrl->sub_arbiter_regbase : noc),
				master_id, val, rw, arbiter);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static ssize_t bandwidth_rd_limit_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	size_t size;

	pm_runtime_get_sync(g_memctrl_dev);

	size = __memctrl_bandwidth_limit_show(dev, attr, buf);

	pm_runtime_put_sync(g_memctrl_dev);

	return size;
}

static ssize_t bandwidth_rd_limit_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	int ret;

	pm_runtime_get_sync(g_memctrl_dev);
	ret = __memctrl_set_bandwidth(dev, buf, R);
	if (!ret) {
		pm_runtime_put_sync(g_memctrl_dev);
		return count;
	}

	pm_runtime_put_sync(g_memctrl_dev);
	memctrl_bandwidth_limit_help();
	return count;
}

static DEVICE_ATTR_RW(bandwidth_rd_limit);

static ssize_t bandwidth_wr_limit_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	size_t size;

	pm_runtime_get_sync(g_memctrl_dev);
	size = __memctrl_bandwidth_limit_show(dev, attr, buf);
	pm_runtime_put_sync(g_memctrl_dev);

	return size;
}

static ssize_t bandwidth_wr_limit_store(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count)
{
	int ret;

	pm_runtime_get_sync(g_memctrl_dev);
	ret = __memctrl_set_bandwidth(dev, buf, W);
	if (!ret) {
		pm_runtime_put_sync(g_memctrl_dev);
		return count;
	}

	pm_runtime_put_sync(g_memctrl_dev);
	memctrl_bandwidth_limit_help();
	return count;
}

static DEVICE_ATTR_RW(bandwidth_wr_limit);

static struct attribute *lombo_memctrl_attributes[] = {
	&dev_attr_memctrl.attr,
	&dev_attr_memctrl_time.attr,
	&dev_attr_memctrl_sample_unit.attr,
	&dev_attr_memctrl_flaw_unit.attr,
	&dev_attr_memctrl_size_unit.attr,
	&dev_attr_set_read_priority.attr,
	&dev_attr_set_write_priority.attr,
	&dev_attr_bandwidth_rd_limit.attr,
	&dev_attr_bandwidth_wr_limit.attr,
#ifdef CONFIG_ARCH_LOMBO_N7V7
	&dev_attr_set_sarb_sr.attr,
	&dev_attr_set_mctrl_sr.attr,
#endif
	NULL,
};

static const struct attribute_group lombo_memctrl_attr_group = {
	.attrs = lombo_memctrl_attributes,
};

int mctrl_conf_sysfs_create_group(struct device *dev, struct device *pdev)
{
	g_memctrl_dev = pdev;
	return sysfs_create_group(&dev->kobj, &lombo_memctrl_attr_group);
}

int of_mctrl_set_default_bwctrl(struct device *dev)
{
	struct device_node *child = NULL;
	const char *master_name;
	int ret, index, i;
	u32 band, io, twin;
	struct clk *axi_clk = NULL;

	if (!dev->of_node) {
		PRT_ERR("no memctrl np\n");
		return -EINVAL;
	}

	child = of_get_child_by_name(dev->of_node, "mctrl_bwctrl");
	if (!child) {
		PRT_DBG("no mctrl_bwctrl np\n");
		return -EINVAL;
	}
#if defined(CONFIG_ARCH_LOMBO_N9V1)
	axi_clk = clk_get(dev, "mem_axi1_clk");
#else
	axi_clk = clk_get(dev, "mem_axi_clk");
#endif
	twin = clk_get_rate(axi_clk) / 1000000;

	index = of_property_count_strings(child, "masters");
	for (i = 0; i < index; i++) {
		ret = of_property_read_string_index(child,
				"masters", i, &master_name);
		ret = of_property_read_u32_index(child, "bandwidth",
							(2*i), &band);
		if (ret) {
			PRT_WARN("%s band[%d] is invalid\n",
					master_name, band);
			continue;
		}
		ret = of_property_read_u32_index(child, "bandwidth",
							(2*i)+1, &io);
		PRT_DBG("[%d]: %s -- %d-%d\n", i, master_name, band, io);
		switch (io) {
		case 0: /* write */
			lombo_memctrl_bw_ctrl_mode(master_name, 2, W);
			lombo_memctrl_bw_timewin(master_name, twin, W);
			lombo_memctrl_bw_datasize(master_name, band, W);
			break;
		case 1: /* read */
			lombo_memctrl_bw_ctrl_mode(master_name, 2, R);
			lombo_memctrl_bw_timewin(master_name, twin, R);
			lombo_memctrl_bw_datasize(master_name, band, R);
			break;
		default:
			/* write */
			lombo_memctrl_bw_ctrl_mode(master_name, 2, W);
			lombo_memctrl_bw_timewin(master_name, twin, W);
			lombo_memctrl_bw_datasize(master_name, band, W);
			/* read */
			lombo_memctrl_bw_ctrl_mode(master_name, 2, R);
			lombo_memctrl_bw_timewin(master_name, twin, R);
			lombo_memctrl_bw_datasize(master_name, band, R);
			break;
		}
	}

	return 0;
}

int of_mctrl_set_default_priority(struct device *dev)
{
	struct device_node *child = NULL;
	const char *master_name;
	int index, i, ret;
	u32 level0, level1, io;
	struct master_priority prio;
	struct master_pos pos;
	struct lombo_memctrl *mctrl = dev->driver_data;

	if (!dev->of_node) {
		PRT_ERR("no memctrl np\n");
		return -EINVAL;
	}

	child = of_get_child_by_name(dev->of_node, "mctrl_priority");
	if (!child) {
		PRT_DBG("no mctrl_priority np\n");
		return -EINVAL;
	}

	index = of_property_count_strings(child, "masters");
	for (i = 0; i < index; i++) {
		of_property_read_string_index(child,
				"masters", i, &master_name);
		of_property_read_u32_index(child, "level", (3*i), &level0);
		of_property_read_u32_index(child, "level", (3*i)+1, &level1);
		of_property_read_u32_index(child, "level", (3*i)+2, &io);
		PRT_DBG("[%d]: %s -- %d-%d-%d\n",
			i, master_name, level0, level1, io);
		/* level1 is 9bits data, range from 0 to 1023 */
		if (level0 < 0 || level0 > 1023) {
			PRT_WARN("%s level0[%d] is out of range(0-1023)\n",
					master_name, level0);
			continue;
		}
		if (level1 < 0 || level1 > 7) {
			PRT_WARN("%s level1[%d] is out of range(0-7)\n",
					master_name, level1);
			continue;
		}
		prio.level0 = level0;
		prio.level1 = level1;
		ret = master_2_channel(master_name, &pos);
		if (ret == -1)
			continue;
		switch (io) {
		case 0: /* write */
			memctrl_set_priority(mctrl, &pos, &prio, true);
			break;
		case 1: /* read */
			memctrl_set_priority(mctrl, &pos, &prio, false);
			break;
		default:
			/* write */
			memctrl_set_priority(mctrl, &pos, &prio, true);
			/* read */
			memctrl_set_priority(mctrl, &pos, &prio, false);
			break;
		}
	}

	return 0;
}
